package vn.edu.saigontech.ncms.model.info;

import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.hibernate.annotations.OptimisticLockType;

import vn.edu.saigontech.ncms.model.BaseEntity;
import vn.edu.saigontech.ncms.model.enumeration.Gender;
import vn.edu.saigontech.ncms.model.program.Level;
import vn.edu.saigontech.ncms.model.program.Program;
import vn.edu.saigontech.ncms.model.result.MoveUpTest;
import vn.edu.saigontech.ncms.model.result.PlacementShift;
import vn.edu.saigontech.ncms.model.result.PlacementTest;
import vn.edu.saigontech.ncms.model.system.Scholarship;
import vn.edu.saigontech.ncms.model.user.User;
import vn.edu.saigontech.ncms.util.DateUtil;

@Entity
@org.hibernate.annotations.Entity(dynamicInsert = true, dynamicUpdate = true, optimisticLock = OptimisticLockType.ALL)
@Table(name = "ncms_student")
@SuppressWarnings("serial")
public class Student extends BaseEntity {

	@Column(name = "first_name", nullable = false)
	private String firstName;

	@Column(name = "last_name", nullable = false)
	private String lastName;
	
	@Column(name = "nick_name")
	private String nickName;

	@Enumerated
	@Column(name = "gender")
	private Gender gender;

	@Column(name = "date_of_birth")
	@Temporal(TemporalType.DATE)
	private Date dateOfBirth;

	@Column(name = "phone_number")
	private String phoneNumber;

	@Column(name = "email")
	private String email;

	@Column(name = "address")
	private String address;

	@ManyToOne
	@JoinColumn(name = "type_id", nullable = false)
	private StudentType type;
	
	@ManyToOne
	@JoinColumn(name = "scholarship_id")
	private Scholarship scholarship;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "program_id")
	private Program program;

	@Column(name = "consulted_date")
	private Date consultedDate;

	@Column(name = "test_date")
	private Date testDate;
	
	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name = "placement_shift_id")
	private PlacementShift placementShift;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "consultant_id")
	private User consultant;

	@Transient
	private Date fromConsultedDate;

	@Transient
	private Date toConsultedDate;

	@Column(name = "comment", length = 4000)
	private String comment;

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "student")
	@OrderBy("offering desc")
	private Set<AbstractReceipt> offeringReceipts = new HashSet<AbstractReceipt>();

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "student")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	private Set<Waiting> waitings = new HashSet<Waiting>();

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "student")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	@OrderBy("date desc")
	private Set<PlacementTest> placementTests = new HashSet<PlacementTest>();

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "student")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	@OrderBy("date desc")
	private Set<MoveUpTest> moveUpTests = new HashSet<MoveUpTest>();
	
	/* halnt */
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	protected AdditionalInfo additionalInfo;
	
	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(name = "ncms_survey_info_student", joinColumns = @JoinColumn( name="student_id"), inverseJoinColumns = @JoinColumn(name="surveyInfo_id"))
	private Set<SurveyInfo> surveys = new HashSet<SurveyInfo>();
	
	public Offering getLastOffering() {
		if (!this.offeringReceipts.isEmpty()) {
			return this.offeringReceipts.iterator().next().getOffering();
		}
		return null;
	}
	
	public Level getLastLevel() {
		Offering offering = getLastOffering();
		if(offering != null) {
			return offering.getLevel();
		}
		return null;
	}
	
	public PlacementTest getLastPlacementTest() {
		if(!this.placementTests.isEmpty()) {
			return this.placementTests.iterator().next();
		} return null;
	}

	public boolean isStudying() {
		boolean ret = false;
		Offering offering = this.getLastOffering();
		if(offering != null) {
			Date date = new Date();
			if (DateUtil.isBetween(date, offering.getStartDate(), offering.getEndDate())) {
				ret = true;
			}
		}
		return ret;
	}

	public Set<AbstractReceipt> getSortedReceipts() {
		TreeSet<AbstractReceipt> receipts = new TreeSet<AbstractReceipt>(
				new Comparator<AbstractReceipt>() {
					@Override
					public int compare(AbstractReceipt o1, AbstractReceipt o2) {
						if (o1.getOffering().getStartDate().before(
								o2.getOffering().getStartDate())) {
							return 1;
						} else {
							return -1;
						}
					}
				});
		receipts.addAll(this.offeringReceipts);
		return receipts;
	}

	public Level getPlacementLevel() {
		if (!this.placementTests.isEmpty()) {
			return this.placementTests.iterator().next().getLevel();
		}
		return null;
	}

	public String getFullName() {
		return new StringBuilder(this.lastName).append(" ").append(
				this.firstName).toString();
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public Date getDateOfBirth() {
		return dateOfBirth;
	}

	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public Gender getGender() {
		return gender;
	}

	public void setGender(Gender gender) {
		this.gender = gender;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public Set<AbstractReceipt> getOfferingReceipts() {
		return offeringReceipts;
	}

	public void setOfferingReceipts(Set<AbstractReceipt> offeringReceipts) {
		this.offeringReceipts = offeringReceipts;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}

	public StudentType getType() {
		return type;
	}

	public void setType(StudentType type) {
		this.type = type;
	}

	public Set<Waiting> getWaitings() {
		return waitings;
	}

	public void setWaitings(Set<Waiting> waitings) {
		this.waitings = waitings;
	}

	public Set<PlacementTest> getPlacementTests() {
		return placementTests;
	}

	public void setPlacementTests(Set<PlacementTest> placementTests) {
		this.placementTests = placementTests;
	}

	public Set<MoveUpTest> getMoveUpTests() {
		return moveUpTests;
	}

	public void setMoveUpTests(Set<MoveUpTest> moveUpTests) {
		this.moveUpTests = moveUpTests;
	}

	public Program getProgram() {
		return program;
	}

	public void setProgram(Program program) {
		this.program = program;
	}

	public Date getConsultedDate() {
		return consultedDate;
	}

	public void setConsultedDate(Date consultedDate) {
		this.consultedDate = consultedDate;
	}

	public Date getTestDate() {
		return testDate;
	}

	public void setTestDate(Date testDate) {
		this.testDate = testDate;
	}

	public User getConsultant() {
		return consultant;
	}

	public void setConsultant(User consultant) {
		this.consultant = consultant;
	}

	public Date getFromConsultedDate() {
		return fromConsultedDate;
	}

	public void setFromConsultedDate(Date fromConsultedDate) {
		this.fromConsultedDate = fromConsultedDate;
	}

	public Date getToConsultedDate() {
		return toConsultedDate;
	}

	public void setToConsultedDate(Date toConsultedDate) {
		this.toConsultedDate = toConsultedDate;
	}

	public Scholarship getScholarship() {
		return scholarship;
	}

	public void setScholarship(Scholarship scholarship) {
		this.scholarship = scholarship;
	}

	public PlacementShift getPlacementShift() {
		return placementShift;
	}

	public void setPlacementShift(PlacementShift placementShift) {
		this.placementShift = placementShift;
	}

	public String getNickName() {
		return nickName;
	}

	public void setNickName(String nickName) {
		this.nickName = nickName;
	}

	public AdditionalInfo getAdditionalInfo() {
		return additionalInfo;
	}

	public void setAdditionalInfo(AdditionalInfo additionalInfo) {
		this.additionalInfo = additionalInfo;
	}
	
	public Set<SurveyInfo> getSurveys() {
		return surveys;
	}

	public void setSurveys(Set<SurveyInfo> surveys) {
		this.surveys = surveys;
	}
}